iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 21
2
Modern Web

你所不知道的各種前端 Debug 技巧系列 第 21

[Day 21] Performance - How Rendering Works

  • 分享至 

  • xImage
  •  

利用 Performance 面板紀錄效能後,會發現其中以主線程 Main 圖表涵蓋了最多資訊,從開始解析 HTML 到最後繪製出頁面,瀏覽器都做了哪些事情呢?

概覽

使用者載入網頁後,瀏覽器經過轉譯、繪製才會顯示內容,大致可分為以下流程:

  • Parse
  • Layout
  • Paint
  • Composite

Parse

載入 HTML 後,瀏覽器會開始進行解析,先將原始的 Response 內容轉換為字串,再利用 Tag 建立 Tokens、Nodes,最終建立 Document Object Model(DOM):

取自 Constructing the Object Model

而解析 HTML 的過程中若遇到 Inline CSS 或是 <link> 載入 CSS 檔,就會經過類似的流程建立出 CSS Object Model(CSSOM):

接著會利用 CSSOM 來算出 DOM 中每一個 Node 的 ComputedStyle,每個 ComputedStyle 都是包含所有 Style property、value 的超大物件,這個過程就是 Recalculate style

在 Elements 面板 Inspect 一個 Node 時點開 Computed 分頁就會看到該 Node 的 ComputedStyle,另外也可以透過 JavaScript 取得:getComputedStyle(element)

 

Layout

Layout 階段會把 DOM 轉換為 Layout tree,結構和 DOM 非常像,但不可見的 <script><link> 或是加上了 display: none; 的 Node 會被排除,而不在 DOM 中的偽元素 ::before::after 會被加入,建立 Layout tree 的結構(此結構常被稱為 Render tree)。

Layout tree 中每個節點叫作 LayoutObject

接著會遍歷 Layout tree 來計算所有 LayoutObject 的位置和大小,根據 LayoutObject 的不同如 LayoutBlock、LayoutInline 等等會有不同的計算方式,其他如字體、Overflow、Scrollbar、float、table、flex 排版、螢幕大小等等位置計算都在此階段完成。

此外在開始 Paint 之前若 LayoutObject 有特定的 Style property 如 transformwill-change 還會另外建立 PaintLayer,而產生 Layer 的過程就是 Update layer tree

 

Paint

Paint 可分為 Paint 和 Raster 兩個階段。

Paint

第一階段並不會真的進行繪製動作,而是先把 Layout tree 轉換為一連串的繪製步驟,其概念和 Canvas 非常像,例如「在 (x, y) 座標畫一個長寬為 (w, h) 的紅色長方形」,而一個繪製步驟稱為一個 DisplayItem。

雖然所有 LayoutObject 的位置、大小、樣式都確定了,但因為 Stacking order 的關係(positionz-index 等等影響元素覆蓋關係的 Style),無法遍歷一次 Layout tree 就建立所有 DisplayItem,因此這個階段會依據 Stacking order 多次遍歷 Layout tree 來產生 DisplayItem 陣列(DisplayItemList)。

就結果而言可以想像為將每個 LayoutObejct 轉換為 DisplayItem 後再以 Stacking order 排序,順序規則可以參考 Stacking context

Raster

第二階段就是依據 Update layer tree 建立的 PaintLayer 和所有繪製步驟(DisplayItemList)繪製出多個 Layer,每個 Layer 都是一張點陣圖:

RRGGBBAA

 

Composite

整個 Paint 階段繪製出多個 Layer 後,經由合成器(Compositor)把所有的 Layer 依照順序合併為一張圖,再交由瀏覽器顯示出來。

瀏覽器將多個 Layer 合併為最終結果

 

回顧

順過一次流程後會發現中間還穿插了幾個步驟,因此更完整的 Rendering 流程應該如下:

  • Recalculate Style
  • Layout
  • Update Layer Tree
  • Paint
  • Composite Layers

Performance 面板中也能看到這幾個階段

 

補充

Layers

瀏覽器為了提升繪製頁面的效能,會盡可能的利用上次繪製的結果,而 Layer 就是用來降低觸發 Layout、Paint 階段的次數,舉一些實際的例子,將部分內容移到額外的 Layer 後,就能對整個 Layer 加入動畫且不需要重繪該 Layer 的內容。

Tiling

Paint 依據 PaintLayer 繪製出所有 Layer 後會進入 Raster 階段算出每個 Layer 的每個像素的顏色,但一個 Layer 可能非常大,不會全部都在視野中,為了加快 Rasterization 的速度,開始前會將 Layer 分為多個區塊(Tile)同時進行 Rasterization。

Impl thread

Raster、Tiling、Composite 階段都會在其他線程中進行(稱為 Impl thread),不會佔用主線程的資源,因此可以發現一個有趣的現象:就算主線程被佔滿,點擊、輸入等操作都沒有反應,頁面還是能夠 Scroll。

有三個 Rasterizer Thread 同時進行

 

Credits

Life of a pixel
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp


上一篇
[Day 20] Performance - Critical Rendering Path
下一篇
[Day 22] Performance - Rendering Optimization
系列文
你所不知道的各種前端 Debug 技巧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言